package vincent.com.layouttest.view;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import vincent.com.layouttest.R;
import vincent.com.layouttest.util.LogUtil;

public class SlidingPiece extends RelativeLayout implements AnimatorListener{
	private String TAG = LogUtil.DEBUG_TAG;
	private ViewGroup mDrawer;
	SlidingState mCurrentState;

	int mSensativeDis;
	boolean isAniating;

	private int mContainerH;
	private int mContainerW;
	private int mDrawerMeasuredH;
	private int mOriginalY = 0;
	private int mOriginalTop = 0;
	private boolean mIsDragingDrawer = false;
	private OnSlidingListener mSlidingListener;
	private int mSlidingHeight = 0;

	private int mIconCount;
	private ImageView[] mIconsArray;

	private int mDrawerBaseline = 0;
	private int mMaxTopline = 0;
	private int mBottomH;


	public SlidingPiece(Context context) {
		this(context, null);
	}

	public SlidingPiece(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlidingPiece(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mSensativeDis = (int)(context.getResources().getDisplayMetrics().density * 50);
		mCurrentState = SlidingState.Top;
	}

	public OnSlidingListener getSlidingListener() {
		return mSlidingListener;
	}

	public void setSlidingListener(OnSlidingListener mSlidingListener) {
		this.mSlidingListener = mSlidingListener;
	}

	public int getSlidingHeight() {
		return mSlidingHeight;
	}

	public void setSlidingHeight(int mSlidingHeight) {
		this.mSlidingHeight = mSlidingHeight;
		ViewCompat.postInvalidateOnAnimation(this);
	}

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
        LogUtil.i(TAG, "onFinishInflate");
		mDrawer = (ViewGroup)findViewById(R.id.drawer_layout);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		LogUtil.i(LogUtil.DEBUG_TAG, "dispatchTouchEvent");
		return super.dispatchTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		if(isAniating)
			return true;
		int left, top, right;
		left = top = right = 0;
		int y = (int)event.getY();
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
                LogUtil.i(LogUtil.DEBUG_TAG, "onTouchEvent ACTION_DOWN y coordinate=" + y);

				/*if(Math.abs(y - mDrawerBaseline) > mSensativeDis){
					LogUtil.i(LogUtil.DEBUG_TAG, "onTouchEvent outof detect area");
					break;
				}
*/
				mOriginalY = y;
				mIsDragingDrawer = true;
				left = mDrawer.getLeft();
				mOriginalTop = top = mDrawer.getTop();
				right = mDrawer.getWidth();
                LogUtil.i(LogUtil.DEBUG_TAG, "Drawer.layout l=" + left + ", t=" + top + ", r=" + right + ", b=" + mDrawerBaseline);
				mDrawer.layout(left, top, right, mDrawerBaseline);
				mDrawer.requestLayout();

				if (mSlidingListener != null) {
					mSlidingListener.onSlidingStart(mDrawerBaseline - top, mDrawerMeasuredH);
				}

				break;
			case MotionEvent.ACTION_MOVE:
                LogUtil.i(TAG, "onTouchEvent ACTION_MOVE y coordinate=" + y);
				if(!mIsDragingDrawer){
					break;
				}

				mCurrentState = SlidingState.Moving;
				left = mDrawer.getLeft();
				top = mOriginalTop + y - mOriginalY;
				right = mDrawer.getWidth();

				//when reach the most top position of sliding piece
				if(top < mMaxTopline){
					top = mMaxTopline;
				}

				//when reach the bottom of sliding height
				if(top > mDrawerBaseline){
					top = mDrawerBaseline;
				}

                LogUtil.i(LogUtil.DEBUG_TAG, "Drawer.layout l=" + left + ", t=" + top + ", r=" + right + ", b=" + mDrawerBaseline);
				mDrawer.layout(left, top, right, mDrawerBaseline);
				mDrawer.requestLayout();

				if(mSlidingListener != null)
				{
					mSlidingListener.onSliding(mDrawerBaseline - top, mDrawerMeasuredH);
				}
				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_OUTSIDE:
                LogUtil.i(TAG, "onTouchEvent ACTION_UP|ACTION_CANCEL|ACTION_OUTSIDE y coordinate=" + y);
				if(!mIsDragingDrawer){
					break;
				}
				doAnimateMove();
				mOriginalY = 0;
				mIsDragingDrawer = false;

				if(mSlidingListener != null){
					if(SlidingState.Normal == mCurrentState){
						mSlidingListener.onSlidingEnd(0, mDrawerMeasuredH);
					}else{
						mSlidingListener.onSlidingEnd(mDrawerMeasuredH, mDrawerMeasuredH);
					}
				}
				break;
		}
		return true;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
        LogUtil.i(TAG, "onSizeChanged newWidth=" + w + " | newHeight=" + h + " | oldWidth=" + oldw + " | oldh=" + oldh);
		mContainerH = h;
		mContainerW = w;

		mDrawerMeasuredH = mDrawer.getMeasuredHeight();

		mBottomH = 0;
		init(mBottomH);
	}

	public void setDrawerBottom(int bottomH){
		mBottomH = bottomH;
		init(mBottomH);
	}

	private void init(int bottomH){
		mDrawerBaseline = mContainerH - bottomH;
		mMaxTopline = mDrawerBaseline - mDrawerMeasuredH;

		int left, top, right, bottom;
		left = top = right = bottom = 0;
		if(SlidingState.Normal == mCurrentState){

			top = mDrawerBaseline;
			bottom = mDrawerBaseline;
			right = mContainerW;
			mDrawer.layout(left, top, right, bottom);
			mDrawer.requestLayout();
		}else if(SlidingState.Top == mCurrentState || SlidingState.Moving == mCurrentState){

			top = mDrawerBaseline - mDrawerMeasuredH;
			bottom = mDrawerBaseline;
			right = mContainerW;
			mDrawer.layout(left, top, right, bottom);
			mDrawer.requestLayout();
		}
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
							int bottom) {

		LogUtil.i(TAG, "onLayout");
		int l = mDrawer.getLeft();
		int t = mDrawer.getTop();
		int r = mDrawer.getRight();
		int b = mDrawer.getBottom();

        super.onLayout(changed, left, top, right, bottom);

		LogUtil.i(LogUtil.DEBUG_TAG, "onLayout mDrawer l=" + l + ", t=" + t + ", r=" + r + ", b=" + mDrawerBaseline);
		if(!(l == 0 && t == 0 && r == 0 && b == 0) && t <= b){
			mDrawer.layout(l, t, r, b);
		}
	}


	/**
	 * do the animation by the down and up Y position
	 */
	void doAnimateMove(){
		int currentTop = mDrawer.getTop();

		int standardTop = (mDrawerBaseline + mMaxTopline)/2;
		if(currentTop >= standardTop && (SlidingState.Top == mCurrentState || SlidingState.Moving == mCurrentState)){//bottom
			move2Bottom(300);
		}else if(currentTop < standardTop && (SlidingState.Normal == mCurrentState || SlidingState.Moving == mCurrentState)){
			move2Top(300);
		}
	}

	void move2Bottom(int duration){
        LogUtil.i(TAG, "move2Bottom");
		mCurrentState = SlidingState.Normal;
		ObjectAnimator ani;

		int offTemp = mDrawerBaseline - mDrawer.getTop();

		ani = ObjectAnimator.ofFloat(mDrawer, "translationY", 0, offTemp).setDuration(duration);
		ani.setInterpolator(new AccelerateDecelerateInterpolator());
		ani.addListener(this);
		ani.start();
	}

	void move2Top(int duration){
		int releaseTop = mDrawer.getTop();

        LogUtil.i(TAG, "move2Top");

		int left = mDrawer.getLeft();
		int top = releaseTop;
		int right = mDrawer.getWidth();
		int bottom = releaseTop + mDrawerMeasuredH;
		LogUtil.i(LogUtil.DEBUG_TAG, "move2Top mDrawer left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom);
		mDrawer.layout(left, top, right, bottom);
		mDrawer.requestLayout();

		mCurrentState = SlidingState.Top;
		ObjectAnimator ani;

		int offTemp = Math.abs(releaseTop - mMaxTopline);
		LogUtil.i(LogUtil.DEBUG_TAG, "move2Top releaseTop=" + releaseTop + ", mMaxTopline=" + mMaxTopline + ", offTemp=" + offTemp);

		ani = ObjectAnimator.ofFloat(mDrawer, "translationY", 0, -offTemp).setDuration(duration);
		ani.setInterpolator(new AccelerateDecelerateInterpolator());
		ani.addListener(this);
		ani.start();
	}

	@Override
	public void onAnimationCancel(Animator arg0) {
		isAniating = false;
	}

	@Override
	public void onAnimationEnd(Animator arg0) {
		int left, top, right, bottom;
		left = mDrawer.getLeft();
		right = mDrawer.getRight();
		isAniating = false;
		switch(mCurrentState){
			case Top:
				top = mMaxTopline;
				bottom = mDrawerBaseline;
				mDrawer.layout(left, top, right, bottom);
				mDrawer.setTranslationY(0);
				break;
			case Normal:
				top = mDrawerBaseline;
				bottom = mDrawerBaseline;
				mDrawer.layout(left, top, right, bottom);
				mDrawer.setTranslationY(0);
				break;
		}

	}

	@Override
	public void onAnimationRepeat(Animator arg0) {

	}

	@Override
	public void onAnimationStart(Animator arg0) {
		isAniating = true;
	}

	public interface OnSlidingListener{
		public void onSlidingStart(int slidingHeight, int totalHeight);
		public void onSliding(int slidingHeight, int totalHeight);
		public void onSlidingEnd(int slidingHeight, int totalHeight);
	}

	enum SlidingState {
		Top,
		Normal,
		Moving
	}
}
